/* eslint-disable @typescript-eslint/no-explicit-any */
import { instantiate, Node, NodePool, Prefab, Sprite, SpriteAtlas, SpriteFrame, tween, Vec3 } from 'cc';
import { CallBackHandler } from '../Model/PoolObj/CallBackHandler';
import { DataMgr } from './DataManager';
import { LogUtil } from '../../Util/LogUtil';
import { LoadingManager } from './LoadingManager';
import { TipsManager } from '../Model/Tips/TipsManager';
import { MathUtil } from '../../Util/MathUtil';
import { Task } from '../../Util/CustomTask';
import { Effect } from '../Model/Effect/Effect';
import { ImagePath } from '../../Game/Config/GameEnum';
import { ModelBase } from '../UI/ModelBase';
import { Quality } from '../../Data/Types';
export interface IPopResEff {
  spriteFrame: string;
  count: number;
  speed: number;
  beginPos: Vec3;
  endPos: Vec3;
  onUpdate?: (begin: number, end: number, ratio: number) => void;
  callBack?: CallBackHandler;
}

export interface IPopSymbolEff {
  symbolId: number;
  spriteFrame: string;
  count: number;
  speed: number;
  beginPos: Vec3;
  endPos: Vec3;
  onUpdate?: (begin: number, end: number, ratio: number) => void;
  callBack?: CallBackHandler;
}

export interface IPopRelicEff {
  relicId: number;
  spriteFrame: string;
  count: number;
  speed: number;
  beginPos: Vec3;
  endPos: Vec3;
  onUpdate?: (begin: number, end: number, ratio: number) => void;
  callBack?: CallBackHandler;
}

export interface IPopMonsterEff {
  monsterId: number;
  count: number;
  speed: number;
  beginPos: Vec3;
  endPos: Vec3;
  onUpdate?: (begin: number, end: number, ratio: number) => void;
  callBack?: CallBackHandler;
}

export class EffectManager extends ModelBase {
  className: string = 'EffectManager';

  // static readonly effecMaxCount: number = 15;
  private cacheEffectList: Map<number, string> = new Map();
  private effectPool: Map<string, NodePool> = new Map();

  /** 获取特效节点 */
  private async getEffectRes(effectId: number): Promise<[Node, string]> {
    let path: string;
    const tb = DataMgr.tables.TbEffect.get(effectId);
    if (this.cacheEffectList.has(effectId)) {
      path = this.cacheEffectList.get(effectId);
    } else {
      path = ImagePath.effect + tb.res;
      if (!tb) {
        LogUtil.w(`特效表不存在 id: ${effectId}`);
        return;
      }
      this.cacheEffectList.set(effectId, path);
      this.effectPool.set(tb.res, new NodePool());
    }
    const pool = this.effectPool.get(tb.res);
    let node: Node;
    if (pool.size() > 0) {
      node = pool.get();
    } else {
      const res = await this.loadDynamicRes<Prefab>(path);
      node = instantiate(res);
    }
    return [node, tb.param];
  }

  /** 播放一个 */
  async playEffect(effectId: number, param?: any, callBack?: CallBackHandler, loop?: number): Promise<void> {
    const [node, effParam]: [Node, string] = await this.getEffectRes(effectId);
    const sp: Effect = node.getComponent(Effect as any);
    sp.reuse(effectId, effParam, param, callBack, loop);
    TipsManager.Singleton.node.addChild(node);
  }

  /** 添加一个特效管理器管理特效 */
  async addEffect(effectId: number, param?: any, callBack?: CallBackHandler, loop?: number): Promise<Node> {
    const [node, effParam]: [Node, string] = await this.getEffectRes(effectId);
    const sp: Effect = node.getComponent(Effect as any);
    sp.reuse(effectId, effParam, param, callBack, loop);
    return node;
  }

  playEffect2(effectId: number, param?: any) {}

  /** 只用来回收自动播放的特效 */
  releaseEffect(effect: Node) {
    const sp: Effect = effect.getComponent(Effect as any);
    const tb = DataMgr.tables.TbEffect.get(sp.effectId);
    const pool = this.effectPool.get(tb.res);
    pool?.put(effect);
    // effect.destroy();
    // this.releaseDynamicRes(path);
  }

  static readonly resNodeMax: number = 30;
  private resNodePool: NodePool = new NodePool();
  private symbolNodePool: NodePool = new NodePool();
  private relicNodePool: NodePool = new NodePool();
  /** 爆资源
   * @param spriteFrame 资源路径
   * @param count 数量
   * @param speed 速度
   * @param beginPos 开始位置
   * @param endPos 结束位置
   * @param callBack 回调
   */
  async popRes(data: IPopResEff) {
    const frame = await LoadingManager.Ins().loadDynamicsRes<SpriteFrame>(data.spriteFrame);
    const nodeExp = await LoadingManager.Ins().loadDynamicsRes<Prefab>('public/effect/other/sprite');
    const parent = TipsManager.Singleton.node;

    if (data.count >= EffectManager.resNodeMax) data.count = EffectManager.resNodeMax;
    const showSpeed = 0.2 / data.speed;
    const moveSpeed = 0.3 / data.speed;
    const hideSpeed = 0.2 / data.speed;

    const task = Task.createASync();
    const nodes = [];
    let ratio: number = 0;
    for (let i = 0; i < data.count; i++) {
      let icon;
      if (this.resNodePool.size() > 0) {
        icon = this.resNodePool.get();
      } else {
        icon = instantiate(nodeExp);
      }
      icon.getComponent(Sprite).spriteFrame = frame;
      parent.addChild(icon);
      icon.setPosition(data.beginPos);
      icon.setScale(0, 0, 0);
      nodes.push(icon);
      const delayTime = MathUtil.randomBetween(0, 0.5) / data.speed;
      const randomPos = MathUtil.randomPointInRect2D(data.beginPos, 100, 100);
      task.add((next) => {
        tween(icon)
          .to(showSpeed, { scale: new Vec3(1, 1, 1), position: randomPos }, { easing: 'backOut' })
          .delay(delayTime)
          .to(moveSpeed, { position: data.endPos }, { easing: 'quadInOut' })
          .to(hideSpeed, { scale: new Vec3(0, 0, 1) }, { easing: 'backIn' })
          .call(() => {
            if (data.onUpdate) data.onUpdate(0, data.count, ++ratio / data.count);
            next();
          })
          .start();
      });
    }
    task.start(() => {
      nodes.forEach((node) => {
        node.removeFromParent();
        this.resNodePool.put(node);
      });
      data.callBack?.execute();
    });
  }

  async popMonster(data: IPopMonsterEff) {
    const nodeExp = await LoadingManager.Ins().loadDynamicsRes<Prefab>('public/effect/other/sprite');
    const frame = await LoadingManager.Ins().loadDynamicsRes<SpriteFrame>(`public/sprite/guide/${data.monsterId}/spriteFrame`);
    const parent = TipsManager.Singleton.node;
    if (data.count >= EffectManager.resNodeMax) data.count = EffectManager.resNodeMax;
    const showSpeed = 0.2 / data.speed;
    const moveSpeed = 0.3 / data.speed;
    const hideSpeed = 0.2 / data.speed;

    const task = Task.createASync();
    const nodes = [];
    let ratio: number = 0;
    for (let i = 0; i < data.count; i++) {
      let icon;
      if (this.resNodePool.size() > 0) {
        icon = this.resNodePool.get();
      } else {
        icon = instantiate(nodeExp);
      }

      icon.getComponent(Sprite).spriteFrame = frame;
      parent.addChild(icon);
      icon.setPosition(data.beginPos);
      icon.setScale(0, 0, 0);
      nodes.push(icon);
      const delayTime = MathUtil.randomBetween(0, 0.5) / data.speed;
      const randomPos = MathUtil.randomPointInRect2D(data.beginPos, 100, 100);
      task.add((next) => {
        tween(icon)
          .to(showSpeed, { scale: new Vec3(1, 1, 1), position: randomPos }, { easing: 'backOut' })
          .delay(delayTime)
          .to(moveSpeed, { position: data.endPos }, { easing: 'quadInOut' })
          .to(hideSpeed, { scale: new Vec3(0, 0, 1) }, { easing: 'backIn' })
          .call(() => {
            if (data.onUpdate) data.onUpdate(0, data.count, ++ratio / data.count);
            next();
          })
          .start();
      });
    }
    task.start(() => {
      nodes.forEach((node) => {
        node.removeFromParent();
        this.resNodePool.put(node);
      });
      data.callBack?.execute();
    });
  }

  async popSymbol(data: IPopSymbolEff) {
    // const frame = await LoadingManager.Ins().loadDynamicsRes<SpriteFrame>(data.spriteFrame);
    const nodeExp = await LoadingManager.Ins().loadDynamicsRes<Prefab>('public/effect/other/mainGrid');
    const symbolData = DataMgr.tables.TbSymbol.get(data.symbolId);
    let bgImgPath = ``;
    switch (symbolData.quality) {
      case Quality.N:
        bgImgPath = ImagePath.goods_icon + `game/common_card_N/spriteFrame`;
        break;
      case Quality.R:
        bgImgPath = ImagePath.goods_icon + `game/common_card_R/spriteFrame`;
        break;
      case Quality.SR:
        bgImgPath = ImagePath.goods_icon + `game/common_card_SR/spriteFrame`;
        break;
      case Quality.SSR:
        bgImgPath = ImagePath.goods_icon + `game/common_card_SSR/spriteFrame`;
        break;
      case Quality.UR:
        bgImgPath = ImagePath.goods_icon + `game/common_card_UR/spriteFrame`;
        break;
    }
    // if (DataMgr.tables.TbSymbolPool.get('Weapon').pool.indexOf(data.symbolId) === -1) {
    //   bgImgPath = ImagePath.goods_icon + 'game/common_card/spriteFrame';
    // } else {
    //   bgImgPath = ImagePath.goods_icon + 'game/common_card2/spriteFrame';
    // }
    const bgFrame = await LoadingManager.Ins().loadDynamicsRes<SpriteFrame>(bgImgPath);

    const parent = TipsManager.Singleton.node;

    if (data.count >= EffectManager.resNodeMax) data.count = EffectManager.resNodeMax;
    const showSpeed = 0.2 / data.speed;
    const moveSpeed = 0.3 / data.speed;
    const hideSpeed = 0.2 / data.speed;

    const task = Task.createASync();
    const nodes = [];
    let ratio: number = 0;
    for (let i = 0; i < data.count; i++) {
      let grid;
      if (this.symbolNodePool.size() > 0) {
        grid = this.symbolNodePool.get();
      } else {
        grid = instantiate(nodeExp);
      }
      const bg = grid.getChildByName('bg');
      bg.getComponent(Sprite).spriteFrame = bgFrame;
      const icon = grid.getChildByName('icon');
      LoadingManager.Ins()
        .loadDynamicsRes<SpriteAtlas>(ImagePath.symbol_atlas)
        .then((atlas) => {
          icon.getComponent(Sprite).spriteFrame = atlas.getSpriteFrame(data.spriteFrame);
        });
      parent.addChild(grid);
      grid.setPosition(data.beginPos);
      grid.setScale(0, 0, 0);
      nodes.push(grid);
      const delayTime = MathUtil.randomBetween(0, 0.5) / data.speed;
      const randomPos = MathUtil.randomPointInRect2D(data.beginPos, 100, 100);
      task.add((next) => {
        tween(grid)
          .to(showSpeed, { scale: new Vec3(1, 1, 1), position: randomPos }, { easing: 'backOut' })
          .delay(delayTime)
          .to(moveSpeed, { position: data.endPos }, { easing: 'quadInOut' })
          .to(hideSpeed, { scale: new Vec3(0, 0, 1) }, { easing: 'backIn' })
          .call(() => {
            if (data.onUpdate) data.onUpdate(0, data.count, ++ratio / data.count);
            next();
          })
          .start();
      });
    }
    task.start(() => {
      nodes.forEach((node) => {
        node.removeFromParent();
        this.symbolNodePool.put(node);
      });
      data.callBack?.execute();
    });
  }

  async popRelic(data: IPopRelicEff) {
    // const frame = await LoadingManager.Ins().loadDynamicsRes<SpriteFrame>(data.spriteFrame);
    const nodeExp = await LoadingManager.Ins().loadDynamicsRes<Prefab>('public/effect/other/mainGridR');
    const relicData = DataMgr.tables.TbRelic.get(data.relicId);
    let bgImgPath = ``;
    switch (relicData.quality) {
      case Quality.N:
        bgImgPath = `N`;
        break;
      case Quality.R:
        bgImgPath = `R`;
        break;
      case Quality.SR:
        bgImgPath = `SR`;
        break;
      case Quality.SSR:
        bgImgPath = `SSR`;
        break;
    }
    // if (DataMgr.tables.TbSymbolPool.get('Weapon').pool.indexOf(data.symbolId) === -1) {
    //   bgImgPath = ImagePath.goods_icon + 'game/common_card/spriteFrame';
    // } else {
    //   bgImgPath = ImagePath.goods_icon + 'game/common_card2/spriteFrame';
    // }
    const atlas = await LoadingManager.Ins().loadDynamicsRes<SpriteAtlas>(ImagePath.relicgrid_atlas);

    const bgFrame = atlas && atlas.getSpriteFrame ? atlas.getSpriteFrame(`relic_grid${bgImgPath}`) : null;

    const parent = TipsManager.Singleton.node;

    if (data.count >= EffectManager.resNodeMax) data.count = EffectManager.resNodeMax;
    const showSpeed = 0.2 / data.speed;
    const moveSpeed = 0.3 / data.speed;
    const hideSpeed = 0.2 / data.speed;

    const task = Task.createASync();
    const nodes = [];
    let ratio: number = 0;
    for (let i = 0; i < data.count; i++) {
      let grid;
      if (this.relicNodePool.size() > 0) {
        grid = this.relicNodePool.get();
      } else {
        grid = instantiate(nodeExp);
      }
      const bg = grid.getChildByName('bg');
      bg.getComponent(Sprite).spriteFrame = bgFrame;
      const icon = grid.getChildByName('icon');
      LoadingManager.Ins()
        .loadDynamicsRes<SpriteAtlas>(ImagePath.symbol_atlas)
        .then((atlas) => {
          icon.getComponent(Sprite).spriteFrame = atlas.getSpriteFrame(data.spriteFrame);
        });
      parent.addChild(grid);
      grid.setPosition(data.beginPos);
      grid.setScale(0, 0, 0);
      nodes.push(grid);
      const delayTime = MathUtil.randomBetween(0, 0.5) / data.speed;
      const randomPos = MathUtil.randomPointInRect2D(data.beginPos, 100, 100);
      task.add((next) => {
        tween(grid)
          .to(showSpeed, { scale: new Vec3(1, 1, 1), position: randomPos }, { easing: 'backOut' })
          .delay(delayTime)
          .to(moveSpeed, { position: data.endPos }, { easing: 'quadInOut' })
          .to(hideSpeed, { scale: new Vec3(0, 0, 1) }, { easing: 'backIn' })
          .call(() => {
            if (data.onUpdate) data.onUpdate(0, data.count, ++ratio / data.count);
            next();
          })
          .start();
      });
    }
    task.start(() => {
      nodes.forEach((node) => {
        node.removeFromParent();
        this.relicNodePool.put(node);
      });
      data.callBack?.execute();
    });
  }
}
